//=======================================================================================
// Name        : tp1.cpp
// Author      : jk, as, md
// Version     : 0.1
// Copyright   : 
// Description : calculo de la raiz 2
// Forma de uso : ./tp1 -p ii -a ff -t ii -o ss -g
// Aclaraciones : [-p] precision de los numeros de punto flotante
//                [-a] una aproximacion a la raiz de 2 para el primer metodo
//                [-t] la cantidad de veces que se va a iterar
//                [-o] el nombre de salida del archivo con los resultados
//				  [-g] si se pasa esta opcion genera los casos que fueron graficados
//=======================================================================================

#include <iostream>
#include <string>
#include <fstream>
#include "Float.h"
using namespace std;
#define APROXRAIZ2 1.4
#define PRECISION 52
#define TERMINOS 170
#define SALIDA "resultados.out"
#define GRAFICOS 0

Float metodoBabilonio(Float aproximacion,int precision,int terminos);
Float metodoBinomial(int precision,int terminos);
//suma los positivos y negativos por separado y dsp los resta
Float metodoBinomial2(int precision,int terminos);

int main(int argc, char* argv[])
{
	int precision=PRECISION;
	Float aprox(precision,APROXRAIZ2);
	int terminos=TERMINOS;
	string salida = SALIDA;
	string s;
	bool graficar = GRAFICOS;
	//toma los argumentos pasados como parametro
	for(int i=0;i<argc;i++)
	{
		s = argv[i]; 
		if(s == "-p")
			precision = atoi(argv[i+1]);
		else
			if(s == "-a")
				aprox = Float(precision,atof(argv[i+1]));
			else
				if(s == "-t")
					terminos = atoi(argv[i+1]);
				else
					if(s == "-o")
						salida = argv[i+1];
					else
						if(s == "-g")
							graficar = true;
	}
	
	//---solo corre esto si pasa la opcion "-g" por linea de comandos---//
	if(graficar)
	{
		long double raizDOUBLE = sqrt(2);
		Float raizFLOAT(PRECISION,sqrt(2));
		ofstream fsfba,fsdba,fsfbin,fsdbin;
	
		//aca vario la precision para 10 terminos
		fsfba.open("varPrec10BabilonioFLOAT.txt");
		fsdba.open("varPrec10BabilonioDOUBLE.txt");
		fsfbin.open("varPrec10BinomialFLOAT.txt");
		fsdbin.open("varPrec10BinomialDOUBLE.txt");
		for(int p=1;p<=PRECISION;p++)
		{
			Float resBab = metodoBabilonio(aprox,p,10);
			Float resBin = metodoBinomial(p,10);
			
			fsfba<<p<<"\t\t"<<(raizFLOAT - resBab).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdba<<p<<"\t\t"<<fabs((raizDOUBLE - resBab.getValue()))/raizDOUBLE<<endl;
			
			fsfbin<<p<<"\t\t"<<(raizFLOAT - resBin).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdbin<<p<<"\t\t"<<fabs((raizDOUBLE - resBin.getValue()))/raizDOUBLE<<endl;
			
		}
		fsfba.close();
		fsdba.close();
		fsfbin.close();
		fsdbin.close();
		
		//aca vario la precision para 50 terminos
		fsfba.open("varPrec50BabilonioFLOAT.txt");
		fsdba.open("varPrec50BabilonioDOUBLE.txt");
		fsfbin.open("varPrec50BinomialFLOAT.txt");
		fsdbin.open("varPrec50BinomialDOUBLE.txt");
		for(int p=1;p<=PRECISION;p++)
		{
			Float resBab = metodoBabilonio(aprox,p,50);
			Float resBin = metodoBinomial(p,50);
			
			fsfba<<p<<"\t\t"<<(raizFLOAT - resBab).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdba<<p<<"\t\t"<<fabs((raizDOUBLE - resBab.getValue()))/raizDOUBLE<<endl;
					
			fsfbin<<p<<"\t\t"<<(raizFLOAT - resBin).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdbin<<p<<"\t\t"<<fabs((raizDOUBLE - resBin.getValue()))/raizDOUBLE<<endl;
			
		}
		fsfba.close();
		fsdba.close();
		fsfbin.close();
		fsdbin.close();
		
		//aca vario la precision para 100 terminos
		fsfba.open("varPrec100BabilonioFLOAT.txt");
		fsdba.open("varPrec100BabilonioDOUBLE.txt");
		fsfbin.open("varPrec100BinomialFLOAT.txt");
		fsdbin.open("varPrec100BinomialDOUBLE.txt");
		for(int p=1;p<=PRECISION;p++)
		{
			Float resBab = metodoBabilonio(aprox,p,100);
			Float resBin = metodoBinomial(p,100);
					
			fsfba<<p<<"\t\t"<<(raizFLOAT - resBab).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdba<<p<<"\t\t"<<fabs((raizDOUBLE - resBab.getValue()))/raizDOUBLE<<endl;
					
			fsfbin<<p<<"\t\t"<<(raizFLOAT - resBin).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdbin<<p<<"\t\t"<<fabs((raizDOUBLE - resBin.getValue()))/raizDOUBLE<<endl;
			
		}
		fsfba.close();
		fsdba.close();
		fsfbin.close();
		fsdbin.close();
		
		//aca vario la precision para 170 terminos
		fsfba.open("varPrec170BabilonioFLOAT.txt");
		fsdba.open("varPrec170BabilonioDOUBLE.txt");
		fsfbin.open("varPrec170BinomialFLOAT.txt");
		fsdbin.open("varPrec170BinomialDOUBLE.txt");
		for(int p=1;p<=PRECISION;p++)
		{
			Float resBab = metodoBabilonio(aprox,p,170);
			Float resBin = metodoBinomial(p,170);
					
			fsfba<<p<<"\t\t"<<(raizFLOAT - resBab).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdba<<p<<"\t\t"<<fabs((raizDOUBLE - resBab.getValue()))/raizDOUBLE<<endl;
					
			fsfbin<<p<<"\t\t"<<(raizFLOAT - resBin).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdbin<<p<<"\t\t"<<fabs((raizDOUBLE - resBin.getValue()))/raizDOUBLE<<endl;
					
		}
		fsfba.close();
		fsdba.close();
		fsfbin.close();
		fsdbin.close();
				
		//aca vario la cantidad de terminos para 10 bits de precision
		fsfba.open("varTer10BabilonioFLOAT.txt");
		fsdba.open("varTer10BabilonioDOUBLE.txt");
		fsfbin.open("varTer10BinomialFLOAT.txt");
		fsdbin.open("varTer10BinomialDOUBLE.txt");
		for(int t=1;t<=TERMINOS;t++)
		{
			Float resBab = metodoBabilonio(aprox,10,t);
			Float resBin = metodoBinomial(10,t);
							
			fsfba<<t<<"\t\t"<<(raizFLOAT - resBab).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdba<<t<<"\t\t"<<fabs((raizDOUBLE - resBab.getValue()))/raizDOUBLE<<endl;
							
			fsfbin<<t<<"\t\t"<<(raizFLOAT - resBin).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdbin<<t<<"\t\t"<<fabs((raizDOUBLE - resBin.getValue()))/raizDOUBLE<<endl;
							
		}
		fsfba.close();
		fsdba.close();
		fsfbin.close();
		fsdbin.close();
		
		//aca vario la cantidad de terminos para 25 bits de precision
		fsfba.open("varTer25BabilonioFLOAT.txt");
		fsdba.open("varTer25BabilonioDOUBLE.txt");
		fsfbin.open("varTer25BinomialFLOAT.txt");
		fsdbin.open("varTer25BinomialDOUBLE.txt");
		for(int t=1;t<=TERMINOS;t++)
		{
			Float resBab = metodoBabilonio(aprox,25,t);
			Float resBin = metodoBinomial(25,t);
									
			fsfba<<t<<"\t\t"<<(raizFLOAT - resBab).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdba<<t<<"\t\t"<<fabs((raizDOUBLE - resBab.getValue()))/raizDOUBLE<<endl;
									
			fsfbin<<t<<"\t\t"<<(raizFLOAT - resBin).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdbin<<t<<"\t\t"<<fabs((raizDOUBLE - resBin.getValue()))/raizDOUBLE<<endl;
									
		}
		fsfba.close();
		fsdba.close();
		fsfbin.close();
		fsdbin.close();
		
		//aca vario la cantidad de terminos para 40 bits de precision
		fsfba.open("varTer40BabilonioFLOAT.txt");
		fsdba.open("varTer40BabilonioDOUBLE.txt");
		fsfbin.open("varTer40BinomialFLOAT.txt");
		fsdbin.open("varTer40BinomialDOUBLE.txt");
		for(int t=1;t<=TERMINOS;t++)
		{
			Float resBab = metodoBabilonio(aprox,40,t);
			Float resBin = metodoBinomial(40,t);
									
			fsfba<<t<<"\t\t"<<(raizFLOAT - resBab).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdba<<t<<"\t\t"<<fabs((raizDOUBLE - resBab.getValue()))/raizDOUBLE<<endl;
									
			fsfbin<<t<<"\t\t"<<(raizFLOAT - resBin).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdbin<<t<<"\t\t"<<fabs((raizDOUBLE - resBin.getValue()))/raizDOUBLE<<endl;
									
		}
		fsfba.close();
		fsdba.close();
		fsfbin.close();
		fsdbin.close();
		
		//aca vario la cantidad de terminos para 52 bits de precision
		fsfba.open("varTer52BabilonioFLOAT.txt");
		fsdba.open("varTer52BabilonioDOUBLE.txt");
		fsfbin.open("varTer52BinomialFLOAT.txt");
		fsdbin.open("varTer52BinomialDOUBLE.txt");
		for(int t=1;t<=TERMINOS;t++)
		{
			Float resBab = metodoBabilonio(aprox,52,t);
			Float resBin = metodoBinomial(52,t);
									
			fsfba<<t<<"\t\t"<<(raizFLOAT - resBab).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdba<<t<<"\t\t"<<fabs((raizDOUBLE - resBab.getValue()))/raizDOUBLE<<endl;
									
			fsfbin<<t<<"\t\t"<<(raizFLOAT - resBin).getAbsValue()/raizFLOAT.getAbsValue()<<endl;
			fsdbin<<t<<"\t\t"<<fabs((raizDOUBLE - resBin.getValue()))/raizDOUBLE<<endl;
									
		}
		fsfba.close();
		fsdba.close();
		fsfbin.close();
		fsdbin.close();
	}
	//---aca termina la parte de los casos que fueron graficados---//

	/*todavia no!!!!
	ofstream tFile;
	tFile.open(salida.c_str());
	if(tFile.good())
	{
		//hacer pruebas de todo tipo
	}
	else
	{
		cout<<"No se pudo abrir el arhivo!!"<<endl;
		return 0;
	}
	*/
	/*
	Float resBab = metodoBabilonio(aprox,precision,terminos);
	Float resBin = metodoBinomial(precision,terminos);
	Float resBin2 = metodoBinomial2(precision,terminos);
	
	Float maxPrecisionBinomial= metodoBinomial(PRECISION,TERMINOS);
	Float maxPrecisionBabilonio= metodoBabilonio(aprox,PRECISION,TERMINOS);
	Float maxPrecisionBabilonio2 = metodoBinomial2(PRECISION,terminos);

	Float raiz(PRECISION,sqrt((double) 2));
	long double res1 = resBab.getValue();
	long double res2 = resBin.getValue();
	long double res3 = resBin2.getValue();
	
	//errores para ver si hay diferencia entre la representacion en Float o un double
	cout<<"Error en Float para babilonio "<<(raiz - resBab).getAbsValue()/raiz.getAbsValue()<<endl;
	cout<<"Error en Float para binomial "<<(raiz - resBin).getAbsValue()/raiz.getAbsValue()<<endl;
	cout<<"Error en Float para binomial2 "<<(raiz - resBin2).getAbsValue()/raiz.getAbsValue()<<endl;
	cout<<"Error en double para babilonio "<<fabs(sqrt((double) 2) - res1)/sqrt((double) 2)<<endl;
	cout<<"Error en double para binomial "<<fabs(sqrt((double) 2) - res2)/sqrt((double) 2)<<endl;
	cout<<"Error en double para binomial2 "<<fabs(sqrt(2) - res3)/sqrt(2)<<endl;
	 */
	
	return 0;
}

Float metodoBabilonio(Float aproximacion,int precision,int terminos)
{
	Float res = aproximacion;
	Float aux(precision,2.00);
	for(int i=0;i<terminos;i++)
	{
		res = (res + (aux/res))/aux;
	}
	
	return res;
}

Float metodoBinomial(int precision,int terminos)
{
	Float res(precision,1.00);
	Float anterior(precision, 0);
	Float uno(precision,1);
	Float fact(precision,1);
	Float ene(precision,0.5);
	Float acum = ene;
	Float it(precision,1);
	
	while (it.getValue() < terminos && res != anterior){
		anterior = res;
		res = res + acum/fact;
		acum = acum*(ene - it);
		it= it + uno;
		fact = fact * it;	
	}
	
	return res;
}

//este metodo no lo usamos xq da lo mismo que el anterior
Float metodoBinomial2(int precision,int terminos)
{
	Float uno(precision,1);
	Float fact(precision,1);
	Float ene(precision,0.5);
	Float acum = ene;
	Float it(precision,1);

	Float positivos(precision,1.00);
	Float negativos(precision,0.00);

	while (it.getValue() < terminos){
		if(acum.getValue() >= 0)
			positivos = positivos + acum/fact;
		else
			negativos = negativos + acum/fact;

		acum = acum*(ene - it);
		it= it + uno;
		fact = fact * it;		
	}
	
	
	return (positivos + negativos);
}
